const _ = require("lodash");
const util = require("util");
const fse = require("fs-extra");
const router = require("koa-router")();
const archiver = require("archiver");
const moment = require("moment");
const AdmZip = require("adm-zip");
const path = require("path");
const uuid = require("uuid");
const Jimp = require("jimp");
const shelljs = require("shelljs");
const fsUtils = require("nodejs-fs-utils");
const prettyBytes = require("pretty-file-bytes");

/**
 * api {post} /home/app/file/mkdir 创建文件夹
 *
 * apiParam {String} dir_name 文件夹名称
 *
 */
router.post("/mkdir", async (ctx, next) => {
  const user = ctx.user;
  const baas = ctx.baas;
  const appid = baas.appid;
  let dir = path.resolve(
    ctx.config("web"),
    appid,
    ctx.post.dir_path,
    ctx.post.dir_name
  );
  try {
    if (!dir) {
      ctx.error("参数有误");
      return;
    }
    // 获取文件夹名称
    const dirName = path.parse(dir).base;
    const pathName = path.resolve(dir, "../");
    // 获取不重复的文件夹名;
    const newName = await ctx.getName("新建文件夹", "", pathName);
    dir = path.join(pathName, newName);
    await fse.ensureDir(dir);
    ctx.success("新建成功");
  } catch (err) {
    console.log("err", err);
    ctx.error(err);
  }
});
/**
 * api {post} /home/app/file/copy 文件拷贝
 *
 * apiParam {String} file_name 文件名称
 * apiParam {String} aims_file 拷贝目标路径
 *
 */
router.post("/copy", async (ctx, nex) => {
  const post = ctx.post;
  const baas = ctx.baas;
  const appid = baas.appid;

  try {
    fileName = post.fileName.split(",");
    // 判断文件夹or文件
    for (const file of fileName) {
      const filePath = path.resolve(ctx.config("web"), appid, file);
      aimsFile = path.join(ctx.config("web"), appid, post.aimsFile);
      if (!filePath || !aimsFile) {
        ctx.error("参数有误");
        return;
      }
      // 禁止文件复制粘贴到自身文件夹下
      if (aimsFile.indexOf(path.join(filePath, "/")) > -1) {
        ctx.error("不可以这样做！");
        return;
      }
      // 遍历目标文件夹是否有重名文件，有，则添加‘副本’
      const type = path.parse(filePath).ext;
      const newFileName = path.parse(filePath).name;
      const newName = await ctx.getName(newFileName + "副本", type, aimsFile);
      await fse.copy(filePath, path.join(aimsFile, newName));
    }
    ctx.success("复制成功");
  } catch (err) {
    console.log("err", err);
    ctx.error(err);
  }
});
/**
 * api {post} /home/app/file/remove 删除文件
 *
 * apiParam {String} fileName 文件路径
 *
 */
router.post("/remove", async (ctx, nex) => {
  let files = ctx.post.fileName;
  const baas = ctx.baas;
  const appid = baas.appid;
  try {
    if (!files) {
      ctx.error("参数有误");
      return;
    }
    // 如果文件数大于1个，则循环
    files = files.split(",");
    for (const file of files) {
      filePath = path.resolve(ctx.config("web"), appid, file);
      const result = await fse.remove(filePath);
    }
    ctx.success("删除成功");
  } catch (err) {
    ctx.error(err);
  }
});
/**
 * api {post} /home/app/file/move 移动文件夹
 *
 * apiParam {String} file 移动的文件路径
 * apiParam {String} aimsFile 移动的文件目标路径
 *
 */
router.post("/move", async (ctx, nex) => {
  const data = ctx.post;
  const baas = ctx.baas;
  const appid = baas.appid;
  const fileList = data.file.split(",");
  const aimsFile = data.aimsFile;
  for (const key in fileList) {
    try {
      if (!fileList[key]) {
        ctx.error("参数有误");
        return;
      }
      fileList[key] = path.resolve(ctx.config("web"), appid, fileList[key]);
      const fileName = path.parse(fileList[key]).base;
      const aimsPath = path.join(ctx.config("web"), appid, aimsFile, fileName);
      // 禁止文件复制粘贴到自身文件夹下
      if (aimsPath.indexOf(path.join(fileList[key], "/")) > -1) {
        ctx.error("不可以这样做！");
        return;
      }
      await fse.move(fileList[key], aimsPath, {
        overwrite: true
      });
      ctx.success({ filePath: path.join(aimsFile, fileName) });
    } catch (err) {
      console.log(err);
      ctx.error(err);
    }
  }
});
/**
 * api {post} /home/app/file/rename 改变文件名
 *
 * apiParam {String} oldName 要修改的文件旧名称
 * apiParam {String} newName 要修改的文件新名称
 *
 */
router.post("/web/rename", async (ctx, nex) => {
  let { oldName, newName } = ctx.post;
  const baas = ctx.baas;
  const appid = baas.appid;
  try {
    if (!oldName || !newName) {
      ctx.error("参数有误");
      return;
    }
    const savePath = path.resolve(ctx.config("web"), appid, oldName, "../");
    const dirList = await fse.readdir(savePath);
    const type = path.parse(newName).ext;
    const getNewName = await new Promise((resolve, reject) => {
      getName(newName, type);
      function getName(fileName, type) {
        const repeat = ctx.repeat(fileName, dirList);
        resolve(repeat);
      }
    });
    if (getNewName) {
      ctx.error("已有同名文件夹");
      return;
    }
    oldName = path.parse(oldName).base;
    oldName = path.resolve(savePath, oldName);
    newName = path.resolve(savePath, newName);
    // 文件名称去除空格
    newName = newName.replace(/\s/g, "");
    await fse.rename(oldName, newName);
    ctx.success("修改成功");
  } catch (err) {
    console.log("err", err);
    ctx.error(err);
  }
});
/**
 * api {post} /home/app/file/fileInfo 获取文件或文件夹信息
 *
 * apiParam {String} pathName 文件路径
 *
 */
router.post("/fileInfo", async (ctx, nex) => {
  let pathName = ctx.post.pathName;
  const baas = ctx.baas;
  const appid = baas.appid;
  pathName = path.join(ctx.config("web"), appid, pathName);
  try {
    if (!pathName) {
      ctx.error("参数有误");
      return;
    }
    let dirNum = 0;
    let fileNum = 0;
    let size = 0;
    // 获取文件大小
    const isDir = await ctx.isDir(pathName);
    if (isDir) {
      const dirList = await fse.readdir(pathName);
      for (const key in dirList) {
        const isDir = await ctx.isDir(path.resolve(pathName, dirList[key]));
        size += fsUtils.fsizeSync(path.resolve(pathName, dirList[key]));
        if (isDir) {
          dirNum++;
        } else {
          fileNum++;
        }
      }
    } else {
      size += fsUtils.fsizeSync(pathName);
    }
    size = prettyBytes(size);
    // 查看文件信息
    const info = await fse.stat(pathName);
    pathName = pathName.replace(path.resolve(ctx.config("web"), appid), "");
    ctx.success({ pathName, dirNum, fileNum, info, size });
  } catch (err) {
    console.log("err", err);
    ctx.error(err);
  }
});
/**
 * api {post} /home/app/file/dirList 读取文件列表
 *
 * apiParam {String} fileName 要读取的文件路径
 * apiParam {String} keyword 要查询的文件名
 *
 */
router.post("/dirList", async (ctx, nex) => {
  const user = ctx.user;
  const baas = ctx.baas;
  const appid = baas.appid;
  const data = ctx.post;
  const domain = await BaaS.Models.domain
    .query({ where: { baas_id: baas.id } })
    .fetch();
  let pathName = data.fileName
    ? path.join(ctx.config("web"), appid, data.fileName)
    : path.resolve(ctx.config("web"), appid);
  try {
    const keyword = data.keyword ? data.keyword : "";
    const dirArr = [];
    let dirList = await new Promise((resolve, reject) => {
      explorer(pathName);
      function explorer(pathName) {
        fse.readdir(pathName, async (err, files) => {
          if (err) {
            ctx.error(err);
            return;
          }
          if (files.length) {
            files.forEach(file => {
              fse.stat(path.resolve(pathName, file), (err, stat) => {
                if (err) {
                  ctx.error(err);
                  return;
                }
                const info = fse.statSync(path.resolve(pathName, file));
                const storage = fsUtils.fsizeSync(path.resolve(pathName, file));
                if (stat.isDirectory()) {
                  // 如果是文件夹遍历
                  dirArr.push({
                    type: "dir",
                    path: path.join(pathName, file),
                    savepath: pathName,
                    name: file,
                    storage: storage,
                    mtimeMs: info.mtimeMs
                  });
                  explorer(path.resolve(pathName, file));
                } else {
                  // 读出所有的文件
                  const type = path.parse(file).ext.substring(1);
                  if (type == "png" || type == "jpg" || type == "gif") {
                    dirArr.push({
                      type: type,
                      path:
                        `http://${domain.name}/` + path.join(pathName, file),
                      savepath: pathName,
                      name: file,
                      storage: storage,
                      mtimeMs: info.mtimeMs
                    });
                  } else {
                    dirArr.push({
                      type: type,
                      path: path.resolve(pathName, file),
                      savepath: pathName,
                      name: file,
                      storage: storage,
                      mtimeMs: info.mtimeMs
                    });
                  }
                  if (dirArr.length == files.length) {
                    resolve(dirArr);
                  }
                }
              });
            });
            if (dirArr.length) {
              resolve(dirArr);
            }
          } else {
            resolve(dirArr);
          }
        });
      }
    });
    if (keyword.length) {
      const dirListArr = [];
      for (const key in dirList) {
        if (dirArr[key].name.indexOf(keyword) > -1) {
          dirListArr.push(dirArr[key]);
        }
      }
      dirList = dirListArr;
    }
    dirList = _.sortBy(dirList, "name");
    // 判断是否在根目录下，否，则替换/
    if (data.fileName) {
      pathName = pathName.replace(path.join(ctx.config("web"), appid, "/"), "");
    } else {
      pathName = pathName.replace(path.join(ctx.config("web"), appid), "");
    }
    // 把文件路径修改成相对路径
    for (const key in dirList) {
      dirList[key].filePath = path.join(
        dirList[key].savepath,
        dirList[key].name
      );
      dirList[key].filePath = dirList[key].filePath.replace(
        path.join(ctx.config("web"), appid, "/"),
        ""
      );
      if (data.fileName) {
        dirList[key].savepath = dirList[key].savepath.replace(
          path.join(ctx.config("web"), appid, "/"),
          ""
        );
      } else {
        dirList[key].savepath = dirList[key].savepath.replace(
          path.join(ctx.config("web"), appid),
          ""
        );
      }

      dirList[key].path = dirList[key].path.replace(
        path.join(ctx.config("web"), appid, "/"),
        ""
      );
    }
    ctx.success({ dirList, pathName });
  } catch (err) {
    console.log("err", err);
    ctx.error(err);
  }
});
/**
 * api {post} /home/app/file/unzip 解压文件
 *
 * apiParam {String} zip_path 要解压的文件
 *
 */
router.post("/unzip", async (ctx, nex) => {
  let zipPath = ctx.post.zip_path;
  const baas = ctx.baas;
  const appid = baas.appid;
  const pathName = path.parse(zipPath).name;
  if (!zipPath) {
    ctx.error("参数有误");
    return;
  }
  zipPath = path.resolve(ctx.config("web"), appid, zipPath);
  const dirPath = path.resolve(zipPath, "../");
  const newSavePath = await ctx.getName(pathName, "", dirPath);
  try {
    // 解压文件
    const zip = new AdmZip(zipPath);
    zip.extractAllTo(path.join(dirPath, newSavePath), true);
    ctx.success("解压成功");
  } catch (err) {
    console.log("err", err);
    ctx.error(err);
  }
});
/**
 * api {post} /home/app/file/dir_zip 压缩文件夹或文件
 *
 * apiParam {String} path_name 要压缩的文件夹路径
 *
 */
router.post("/dir_zip", async (ctx, nex) => {
  const baas = ctx.baas;
  const appid = baas.appid;
  let pathName = ctx.post.path_name;
  pathName = path.resolve(ctx.config("web"), appid, pathName);
  const pathArr = pathName.split(",");
  const pathDirList = path.resolve(pathName, "../");
  // 遍历文件夹，获取不重复文件名称
  const fileArr = path.parse(pathName).base.split(",");
  const fileName = path.parse(pathName).base.replace(",", " ");
  const pathBaseName = path.parse(pathName).name;
  let getNewName = await ctx.getName(pathBaseName, ".zip", pathDirList);
  // 如果是多个文件，压缩的文件名为归档*
  if (fileArr.length > 1) {
    getNewName = await ctx.getName("归档", ".zip", pathDirList);
  }
  // 执行shell命令，压缩文件
  shelljs.exec(`cd ${pathDirList} && zip -r ${getNewName} ${fileName}`);

  ctx.success("压缩成功");
});
/**
 * api {get} /home/app/file/download 下载文件
 *
 * apiParam {String} fileName 要下载的文件相对路径
 *
 */
router.get("/download", async (ctx, nex) => {
  const files = ctx.query.fileName;
  const baas = ctx.baas;
  // 分割开多文件为数组
  const fileArr = files.split(",");
  const basePath = path.resolve(
    ctx.config("web"),
    baas.appid,
    fileArr[0],
    "../"
  );
  // 获取文件名称newName做临时存放要下载的文件，newName2下载的压缩文件名称
  const newName = await ctx.getName("归档", "", basePath);
  const fileName = path.join(basePath, newName);
  const newPath = path.join(basePath, newName);
  await fse.ensureDir(newPath);
  const newName2 = await ctx.getName("归档", ".zip", basePath);
  // 循环把要下载的文件复制到新文件夹下
  for (const key in fileArr) {
    const pathName = path.resolve(ctx.config("web"), baas.appid, fileArr[key]);
    await fse.copy(pathName, path.join(newPath, fileArr[key]));
  }
  // 执行shell命令，压缩文件
  shelljs.exec(`cd ${basePath} && zip -r ${newName2} ${newName}`);
  const fileName2 = path.join(basePath, newName2);
  // 设置头信息，下载压缩文件
  const userAgent = (ctx.headers["user-agent"] || "").toLowerCase();
  try {
    // 兼容不同浏览器解析中文名
    if (userAgent.indexOf("msie") >= 0 || userAgent.indexOf("chrome") >= 0) {
      ctx.set(
        "Content-Disposition",
        "attachment; filename=" + encodeURIComponent("归档.zip")
      );
    } else if (userAgent.indexOf("firefox") >= 0) {
      /* eslint-disable quotes */
      ctx.set(
        "Content-Disposition",
        "attachment; filename*=\"utf8''" + encodeURIComponent("归档.zip") + '"'
      );
      /* eslint-disable quotes */
    } else {
      ctx.set(
        "Content-Disposition",
        "attachment; filename=" + new Buffer("归档.zip").toString("binary")
      );
    }
    ctx.body = await fse.createReadStream(fileName2);
    await fse.remove(fileName);
    await fse.remove(fileName2);
  } catch (err) {
    console.log(err);
    ctx.error(err);
  }
});
/**
 * api {post} /home/app/file/uploads 上传文件
 *
 * apiParam {File} file 要上传的文件
 *
 */
router.post("/web/uploads", async (ctx, nex) => {
  const baas = ctx.baas;
  const appid = baas.appid;
  const pathName = ctx.query.pathName;
  if (!ctx.file && !ctx.file.file) {
    throw new Error("Upload Image Error");
  }
  const file = ctx.file.file;
  // 文件大小单位转成M
  const fileSize = parseFloat(file.size / Math.pow(1024, 2)).toFixed(2);
  if (fileSize > ctx.config("maxFile")) {
    ctx.error("文件过大");
    return;
  }
  let ext = "";
  switch (file.type) {
    case "image/gif":
      ext = "gif";
      break;
    case "image/jpg":
      ext = "jpg";
      break;
    case "image/pjpeg":
      ext = "jpg";
      break;
    case "image/jpeg":
      ext = "jpg";
      break;
    case "image/png":
      ext = "png";
      break;
    case "image/x-png":
      ext = "png";
      break;
    case "application/json":
      ext = "json";
      break;
    case "application/octet-stream":
      ext = "json";
      break;
    case "application/zip":
      ext = "zip";
      break;
    case "application/x-zip-compressed":
      ext = "zip";
      break;
    case "iapplication/javascript":
      ext = "js";
      break;
    case "application/x-sql":
      ext = "sql";
      break;
    default:
      ext = path.parse(file.type).name;
      break;
  }
  let savename = uuid() + "." + ext;
  if (ext != "jpg" && ext != "png") {
    savename = file.name;
  }
  // 判断是否有同名文件
  const aimsPath = path.join(ctx.config("web"), appid, pathName);
  const dirList = await fse.readdir(aimsPath);
  const sameFile = await new Promise((resolve, reject) => {
    getName(savename, "");
    function getName(fileName) {
      // 判断该文件夹下是否有重名文件
      const repeat = ctx.repeat(fileName, dirList);
      resolve(repeat);
    }
  });
  if (sameFile) {
    ctx.error("已有同名文件");
    return;
  }
  await fse.move(file.path, path.join(aimsPath, savename));
  Jimp.read(aimsPath)
    .then(lenna => {
      lenna.quality(30).write(ctx.config("web"), appid, pathName, savename);
    })
    .catch(err => {
      console.error(err);
    });

  ctx.success({
    file_name: savename,
    path: savename
  });
});
/**
 * api {post} /home/app/file/web/domain 上传域名
 *
 * apiParam {String} name 要上传的域名
 *
 */
router.post("/web/domain", async (ctx, nex) => {
  const baas = ctx.baas;
  const { id, name } = ctx.post;
  const isEmpty = ctx.isEmpty({ name: name }, ["name"]);
  if (!isEmpty) {
    ctx.error("请完善表单");
    return;
  }
  // 修改主机配置文件
  await fse.outputFile(
    path.resolve(ctx.config("nginx"), `${baas.appid}.conf`),
    `server {
  listen 80;
  server_name ${name}; #主机名称，绑定的域名
  root /www/baas-web/${baas.appid};

  location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
      expires 30d;
  }

  location ~ .*\.(js|css)$ {
      expires 10d;
  }

  location / {
      index index.html index.htm index_prod.html;
  }
}`
  );
  await BaaS.Models.domain
    .forge({ id: id, baas_id: baas.id, name: name })
    .save();
  shelljs.exec("/usr/sbin/nginx -s reload");
  ctx.success("保存成功");
});
/**
 * api {post} /home/app/file/web/domain 获取域名
 *
 * apiParam {Number} id 要查询的域名id
 *
 */
router.get("/web/domain", async (ctx, nex) => {
  const baas = ctx.baas;
  const domain = await BaaS.Models.domain
    .query({ where: { baas_id: baas.id } })
    .fetch();
  ctx.success(domain);
});
/**
 * api {post} /home/app/file/web/readFile 读取文件内容
 *
 * apiParam {String} filePath 要读取的文件路径
 *
 */
router.post("/web/readFile", async (ctx, nex) => {
  let { filePath } = ctx.post;
  const baas = ctx.baas;
  filePath = path.resolve(ctx.config("web"), baas.appid, filePath);
  const data = await fse.readFile(filePath, "utf-8");
  ctx.success(data);
});
/**
 * api {post} /home/app/file/web/writeFile 写入文件内容
 *
 * apiParam {String} filePath 要写入的文件路径
 *
 */
router.post("/web/writeFile", async (ctx, nex) => {
  const { filePath, data } = ctx.post;
  const baas = ctx.baas;
  const fileName = path.resolve(ctx.config("web"), baas.appid, filePath);
  const result = await fse.writeFile(fileName, data);
  ctx.success(result);
});

module.exports = router;
